package com.netty.demo.server.netty.tcp;

import com.netty.demo.server.netty.constant.ConstantValue;
import com.netty.demo.server.netty.protocol.RequestProtocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * 请求解码器
 * <p>
 * 请求解码器
 * <pre>
 * 数据包格式
 * +——----——+——-----——+——----——+——----——+——-----——+
 * | 包头  :  协议版本  :  协议长度   :   消息id  :   发送时间   :  数据       |
 * +——----——+——-----——+——----——+——----——+——-----——+
 * </pre>
 * 协议开始标志 HEAD，为int类型的数据，16进制表示为0X110
 * 模块号2字节short
 * 命令号2字节short
 * 传输数据的长度  length int类型
 *
 * @author 余秀良
 * @version 1.0
 * <p>date: 2019年09月09日  14:42 </p>
 * <p>address: 四川成都 </p>
 */
@Slf4j
@Component
public class TcpRequestDecoder extends ByteToMessageDecoder {
    /**
     * TODO 目前为测试数据, 以正式数据为准
     * 数据包基本长度
     * byte     1字节
     * short    2字节
     * int      4字节
     * long     8字节
     * char     2字节
     * float    4字节
     * double   8字节
     */
    private static final int BASE_LENGTH = 4 + 2 + 2 + 4;


    protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) {
        //获取对 DatagramPacket 中的数据（ByteBuf）的引用
        ByteBuf byteBuf = msg.copy().content();
        try {
            //包头
            int packageTag = byteBuf.readInt();
            if (packageTag != ConstantValue.PACKAGE_HEAD) {
                sendMessage(ctx, msg, "非法协议");
                return;
            }
            //可读长度readableBytes必须大于基本长度才处理
            if (byteBuf.readableBytes() >= BASE_LENGTH) {
                //防止socket字节流攻击
                int max = 2048;
                if (byteBuf.readableBytes() > max) {
                    // 忽略所有可读的字节
                    byteBuf.skipBytes(byteBuf.readableBytes());
                    sendMessage(ctx, msg, "帧数据超长");
                    return;
                }
                //TODO 具体数据先后顺序 等协议确定了以后再调整
                //协议    包头:协议版本:协议长度:消息id:发送时间:数据
                //协议版本
                int version = byteBuf.readInt();
                //协议长度
                int length = byteBuf.readInt();
                //将消息id
                short messageId = byteBuf.readUnsignedByte();
                //发送时间
                long time = byteBuf.readLong();
                //命令号
                short command = byteBuf.readShort();
                //SEPARATOR
                byteBuf.readByte();
                //获取读索引的当前位置，就是分隔符的索引+1
                int idx = byteBuf.readerIndex();
                //提取消息，从读索引开始，到最后为数据
                String body = byteBuf.slice(idx, byteBuf.readableBytes()).toString(CharsetUtil.UTF_8);
                //构建一个新的 RequestProtocol 对象，并且将它添加到（已经解码的消息的）列表中
                RequestProtocol event = new RequestProtocol();
                event.setLength(length);
                event.setMessageId(messageId);
                event.setData(body);
                event.setSourceAddress(msg.sender());
                event.setRecipient(msg);
                //作为本handler的处理结果，交给后面的handler进行处理
                out.add(event);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            byteBuf.clear();
            ReferenceCountUtil.release(byteBuf);
        }
    }

    private void sendMessage(ChannelHandlerContext ctx, DatagramPacket packet, String message) {
        //帧数据超长
        DatagramPacket datagramPacket = new DatagramPacket(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8), packet.sender());
        ctx.channel().writeAndFlush(datagramPacket);
    }


    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
       /* if (in.readableBytes() > 0) {
            // 待处理的消息包
            byte[] bytesReady = new byte[in.readableBytes()];
            in.readBytes(bytesReady);
            //这之间可以进行报文的解析处理
            out.add(bytesReady);
        }*/
        // 可读长度必须大于基本长度
        if (in.readableBytes() >= BASE_LENGTH) {
            // 防止socket字节流攻击
            // 防止，客户端传来的数据过大
            // 因为，太大的数据，是不合理的
            if (in.readableBytes() > 2048) {
                in.skipBytes(in.readableBytes());
            }

            // 记录包头开始的index
            int beginReader;

            while (true) {
                // 获取包头开始的index
                beginReader = in.readerIndex();
                // 标记包头开始的index
                in.markReaderIndex();
                // 读到了协议的开始标志，结束while循环
                if (in.readInt() == ConstantValue.PACKAGE_HEAD) {
                    break;
                }

                // 未读到包头，略过一个字节
                // 每次略过，一个字节，去读取，包头信息的开始标记
                in.resetReaderIndex();
                in.readByte();

                // 当略过，一个字节之后，
                // 数据包的长度，又变得不满足
                // 此时，应该结束。等待后面的数据到达
                if (in.readableBytes() < BASE_LENGTH) {
                    return;
                }
            }

            // 消息的长度

            int length = in.readInt();
            // 判断请求数据包数据是否到齐
            if (in.readableBytes() < length) {
                // 还原读指针
                in.readerIndex(beginReader);
                return;
            }

            // 读取data数据
            byte[] data = new byte[length];
            in.readBytes(data);

            RequestProtocol protocol = new RequestProtocol();
            protocol.setData(Arrays.toString(data));
            out.add(protocol);
        }
    }
}
